#pragma once

#include <iostream>
#include <string>
#include <memory>

namespace Protocol
{
    // x op y
    const std::string ProtSep = " ";
    const std::string LinkBreakSep = "\r\n";
    // x op y -> len\nx op y\n
    std::string Encode(const std::string &message)
    {
        int len = message.size();
        std::string buffer = std::to_string(len) + LinkBreakSep + message + LinkBreakSep;
        return buffer;
    }

    // 判断合法  并且len\nx op y\n ->x op y
    bool Decode(std::string &package, std::string *message)
    {
        auto pos = package.find(LinkBreakSep);
        if (pos == std::string::npos)
            return false;

        std::string messagelen = package.substr(0, pos);
        int len = stoi(messagelen);
        int total = messagelen.size() + len + 2 * LinkBreakSep.size();

        if (package.size() < total)
            return false;

        *message = package.substr(pos + LinkBreakSep.size(), len);
        package.erase(0, total);

        return true;
    }

    class Request
    {
    public:
        Request() : _data_x(0), _data_y(0), _oper(0) {}
        Request(int data_x, int data_y, char oper)
            : _data_x(data_x), _data_y(data_y), _oper(oper)
        {
        }
        void debug()
        {
            std::cout << "_data_x:" << _data_x << std::endl;
            std::cout << "_oper:" << _oper << std::endl;
            std::cout << "_data_y:" << _data_y << std::endl;
        }

        // 结构化字段->序列化
        // x op y
        bool Serialize(std::string *out)
        {
            *out = std::to_string(_data_x) + ProtSep + _oper + ProtSep + std::to_string(_data_y);
            return true;
        }

        // x op y
        bool DeSerialize(std::string &in)
        {
            auto left = in.find(ProtSep);
            if (left == std::string::npos)
                return false;

            auto right = in.rfind(ProtSep);
            if (right == std::string::npos)
                return false;

            _data_x = std::stoi(in.substr(0, left));
            _data_y = std::stoi(in.substr(right + ProtSep.size()));
            std::string oper = in.substr(left + ProtSep.size(), right - (left + ProtSep.size()));
            if (oper.size() != 1)
                return false;
            _oper = oper[0];
            return true;
        }

        int GetX() { return _data_x; }
        int GetY() { return _data_y; }
        char GetOp() { return _oper; }

    private:
        int _data_x;
        int _data_y;
        char _oper;
    };

    class Response
    {
    public:
        Response() : _result(0), _code(0) {}
        Response(int result, int code)
            : _result(result), _code(code)
        {
        }
        void debug()
        {
            std::cout << "_result:" << _result << std::endl;
            std::cout << "_code:" << _code << std::endl;
        }

        bool Serialize(std::string *out)
        {
            *out = std::to_string(_result) + ProtSep + std::to_string(_code);
            return true;
        }

        bool DeSerialize(std::string &in)
        {
            auto pos = in.find(ProtSep);
            if (pos == std::string::npos)
                return false;
            _result = std::stoi(in.substr(0, pos));
            _code = std::stoi(in.substr(pos + ProtSep.size()));
            return true;
        }

        void Setcode(int code)
        {
            _code = code;
        }
        void SetResult(int result)
        {
            _result = result;
        }
        int Getresult() { return _result; }
        int Getcode() { return _code; }

    private:
        int _result;
        int _code;
    };

    class Factory
    {
    public:
        static std::shared_ptr<Request> BuildRequest()
        {
            std::shared_ptr<Request> req(new Request());
            return req;
        }

        static std::shared_ptr<Request> BuildRequest(int x, int y, char op)
        {
            std::shared_ptr<Request> req(new Request(x, y, op));
            return req;
        }

        static std::shared_ptr<Response> BuildResponse()
        {
            std::shared_ptr<Response> resp(new Response());
            return resp;
        }

        static std::shared_ptr<Response> BuildResponse(int result, int code)
        {
            std::shared_ptr<Response> resp(new Response(result, code));
            return resp;
        }
    };
}
